from tqdm import tqdm
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import requests
import math
import time
import matplotlib.ticker as ticker
import os
from datetime import datetime, timedelta

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置中文字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
# 创建图片保存目录
os.makedirs('分析表格/走势图', exist_ok=True)


# 爬取数据
def get_fund_data_corrected(fund_codee, page_count=5):
    """正确获取基金历史净值数据"""
    all_data = []
    for page in tqdm(range(1, page_count + 1), desc=f"Loading", colour='#84ffbc'):  # 进度条
        url = f"http://api.fund.eastmoney.com/f10/lsjz?fundCode={fund_codee}&pageIndex={page}&pageSize=20"
        headers = {
            'Referer': 'http://fundf10.eastmoney.com',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        }
        try:
            response = requests.get(url, headers=headers)
            if response.status_code != 200:
                print(f"请求失败，状态码: {response.status_code}")
                continue
            data = response.json()
            if 'Data' not in data or 'LSJZList' not in data['Data']:
                print(f"基金 {fund_codee} 第{page}页数据格式错误")
                continue
            lsjz_list = data['Data']['LSJZList']
            if not lsjz_list:
                print(f"基金 {fund_codee} 第{page}页无数据")
                break
            # 直接收集数据，不反转
            for item in lsjz_list:
                if item['DWJZ']:  # 确保净值不为空
                    all_data.append({
                        'date': item['FSRQ'],
                        'net_value': float(item['DWJZ'])
                    })
            # time.sleep(0.5)  # 避免请求过快
        except Exception as e:
            print(f"基金 {fund_codee} 第{page}页获取失败: {e}")
            continue
    if not all_data:
        print(f"基金 {fund_codee} 无法获取任何数据")
        return None, None
    # 创建DataFrame并确保正确排序
    df = pd.DataFrame(all_data)
    df['date'] = pd.to_datetime(df['date'])
    df = df.sort_values('date').reset_index(drop=True)  # 按日期正序排列
    print(f"总数据量: {len(df)} 条")
    return df['net_value'].tolist(), df['date'].dt.strftime('%Y-%m-%d').tolist()


# 创建走势图
def create_fund_chart(df, trades, jj_name, jj_code, jj_type, all_result, save_path):
    """创建类似支付宝的基金走势图"""
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 9.4), layout='tight')
    # 设置图表标题
    fig.suptitle(f'{jj_name}({jj_code}) 板块={jj_type}', fontsize=12, fontweight='bold')
    # 过滤数据，只包含有净值的数据点（实际交易日）
    valid_data = df.dropna(subset=['net_value', 'MA20', 'Upper', 'Lower']).copy()
    # 子图1：净值走势图（类似支付宝主图）
    ax1.plot(valid_data['date'], valid_data['net_value'], label='基金净值', linewidth=2, color='#1f77b4', alpha=1)
    ax1.plot(valid_data['date'], valid_data['MA20'], label='20日均线', linestyle='--', color='#ff7f0e', alpha=0.7)
    ax1.plot(valid_data['date'], valid_data['Upper'], label='布林带上轨', linestyle='--', color='#d62728', alpha=0.5)
    ax1.plot(valid_data['date'], valid_data['Lower'], label='布林带下轨', linestyle='--', color='#2ca02c', alpha=0.5)
    # 标记买卖点
    if trades:
        buy_dates = [trade['date'] for trade in trades if trade['action'] == '买入']
        buy_prices = [trade['price'] for trade in trades if trade['action'] == '买入']
        sell_dates = [trade['date'] for trade in trades if trade['action'] in ['卖出', '清仓止损', 'FINAL_SELL']]
        sell_prices = [trade['price'] for trade in trades if trade['action'] in ['卖出', '清仓止损', 'FINAL_SELL']]
        if buy_dates:
            ax1.scatter(buy_dates, buy_prices, color='green', marker='.', s=40, label='买入点', zorder=5)
        if sell_dates:
            ax1.scatter(sell_dates, sell_prices, color='red', marker='.', s=50, label='卖出点', zorder=5)
    # 增加y轴刻度密度
    ax1.yaxis.set_major_locator(ticker.MaxNLocator(9))  # 最多12个主要刻度
    ax1.yaxis.set_minor_locator(ticker.AutoMinorLocator(1))  # 每个主刻度间2个次要刻度
    ax1.set_title('基金净值走势图', fontsize=10, fontweight='bold')
    ax1.set_ylabel('净值', fontsize=10)
    ax1.legend()
    ax1.grid(True, alpha=0.4)
    ax1.tick_params(axis='x', rotation=0)
    # 创建注释文本
    annot1 = ax1.annotate("", xy=(0, 0), xytext=(2, 2), textcoords="offset points",
                          bbox=dict(boxstyle="round", fc="w", alpha=0.8),
                          arrowprops=dict(arrowstyle="->"))
    annot1.set_visible(False)

    # 鼠标移动事件处理函数
    def on_mouse_move(event):
        if event.inaxes == ax1:
            # 找到最近的数据点
            xdata = valid_data['date'].values
            ydata = valid_data['net_value'].values
            if event.xdata:
                xdata_numeric = np.array([pd.Timestamp(x).timestamp() for x in xdata])
                event_xnumeric = event.xdata * 24 * 3600  # 将matplotlib日期转换为秒
                # 找到最近的日期点
                idx = np.argmin(np.abs(xdata_numeric - event_xnumeric))
                if idx < len(xdata):
                    # 将numpy.datetime64转换为Python datetime对象
                    date_obj = pd.Timestamp(xdata[idx]).to_pydatetime()
                    annot1.xy = (xdata[idx], ydata[idx])
                    annot1.set_text(f"日期: {date_obj.strftime('%Y-%m-%d')}\n净值: {ydata[idx]:.4f}")
                    annot1.set_visible(True)
                    # 添加十字虚线
                    if hasattr(on_mouse_move, 'vline') and on_mouse_move.vline:
                        on_mouse_move.vline.remove()
                        on_mouse_move.hline.remove()
                    on_mouse_move.vline = ax1.axvline(x=xdata[idx], color='black', linestyle='--', alpha=0.3)
                    on_mouse_move.hline = ax1.axhline(y=ydata[idx], color='black', linestyle='--', alpha=0.3)
                    fig.canvas.draw_idle()
        else:
            annot1.set_visible(False)
            # 移除十字虚线
            if hasattr(on_mouse_move, 'vline') and on_mouse_move.vline:
                on_mouse_move.vline.remove()
                on_mouse_move.hline.remove()
                on_mouse_move.vline = None
                on_mouse_move.hline = None
                fig.canvas.draw_idle()

    # 初始化十字虚线变量
    on_mouse_move.vline = None
    on_mouse_move.hline = None
    # 注册事件
    fig.canvas.mpl_connect("motion_notify_event", on_mouse_move)
    # 子图2：资产变化曲线
    strategy_return = all_result['总收益率']
    buy_hold_return = all_result['买入持有收益']
    # 计算策略和买入持有的资产曲线
    initial_capital = 10000
    strategy_final = initial_capital * (1 + strategy_return / 100)
    buy_hold_final = initial_capital * (1 + buy_hold_return / 100)
    # 简化资产曲线（实际应该用回测中的每日数据）
    days = len(valid_data) - 20
    strategy_growth = np.linspace(initial_capital, strategy_final, days)
    buy_hold_growth = np.linspace(initial_capital, buy_hold_final, days)
    dates = valid_data['date'].iloc[20:]
    ax2.plot(dates, strategy_growth, label=f'策略资产 (最终: {strategy_final:.0f}元)',
             linewidth=2, color='#e377c2')
    ax2.plot(dates, buy_hold_growth, label=f'买入持有 (最终: {buy_hold_final:.0f}元)',
             linewidth=2, color='#17becf', linestyle='--')
    ax2.axhline(y=initial_capital, color='gray', linestyle=':', alpha=0.7, label='初始资金')
    ax2.set_title('资产增长对比', fontsize=10, fontweight='bold')
    ax2.set_ylabel('资产价值(元)', fontsize=10)
    ax2.legend()
    ax2.grid(True, alpha=0.3)
    ax2.tick_params(axis='x', rotation=45)
    plt.tight_layout()
    plt.savefig(save_path, dpi=300, bbox_inches='tight')
    plt.show(block=True) # 显示走势图
    # 设置窗口居中
    print(f"📊走势图已保存: {save_path}")


def backtest_simple_strategy(fund_c, name, fund_t='默认', initial_capital=10000, period=60):
    """简单的策略回测"""
    print('🏆' * 25)
    print(f"🔍正在回测基金:{name}({fund_c})")
    # 获取数据·························································
    jz_list, date_list = get_fund_data_corrected(fund_c, page_count=12*2)
    if not jz_list or len(jz_list) < period + 50:
        print(f"数据不足，需要至少{period + 50}天数据，当前只有{len(jz_list) if jz_list else 0}天")
        return None
    # 创建DataFrame
    df = pd.DataFrame({
        'date': date_list,
        'net_value': jz_list
    })
    df['date'] = pd.to_datetime(df['date'])
    df = df[df['date'] <= datetime.now()]
    if len(df) < period + 20:
        print("过滤后数据不足")
        return None
    print(f"有效数据期间: {df['date'].iloc[0].strftime('%Y-%m-%d')} 至 {df['date'].iloc[-1].strftime('%Y-%m-%d')}")
    # 计算技术指标
    df['MA20'] = df['net_value'].rolling(window=period).mean()
    df['STD20'] = df['net_value'].rolling(window=period).std()
    df['Upper'] = df['MA20'] + 2 * df['STD20']
    df['Lower'] = df['MA20'] - 2 * df['STD20']
    # 回测参数和逻辑
    cash = initial_capital
    shares = 0
    cost_basis = 0
    transaction_cost = 0.0015
    trades = []
    portfolio_value = [initial_capital]
    for i in range(period, len(df)):
        if pd.isna(df.iloc[i]['Upper']) or pd.isna(df.iloc[i]['Lower']):
            continue
        current_price = df.iloc[i]['net_value']
        upper = df.iloc[i]['Upper']
        lower = df.iloc[i]['Lower']
        ma20 = df.iloc[i]['MA20']
        signal, profit_pct = generate_trading_signal_simple(current_price, lower, upper, ma20, cost_basis)
        # 交易执行逻辑（保持不变）
        if signal == "BUY" and cash > current_price * 100:
            invest_amount = cash# * 0.5
            shares_to_buy = int(invest_amount / current_price)
            if shares_to_buy > 0:
                buy_amount = shares_to_buy * current_price
                fee = buy_amount * transaction_cost
                cash -= (buy_amount + fee)
                if shares > 0:
                    total_value = (shares * cost_basis) + buy_amount
                    shares += shares_to_buy
                    cost_basis = total_value / shares
                else:
                    shares = shares_to_buy
                    cost_basis = current_price
                trades.append({
                    'date': df.iloc[i]['date'],
                    'action': '买入',
                    'price': current_price,
                    'shares': shares_to_buy,
                    'amount': buy_amount
                })
        elif signal == "SELL" and shares > 0:  # 符合条件全抛
            shares_to_sell = int(shares)# * 0.5)  # 0.5
            if shares_to_sell > 0:
                sell_amount = shares_to_sell * current_price
                fee = sell_amount * transaction_cost
                cash += (sell_amount - fee)
                shares -= shares_to_sell
                trades.append({
                    'date': df.iloc[i]['date'],
                    'action': '卖出',
                    'price': current_price,
                    'shares': shares_to_sell,
                    'amount': sell_amount
                })
        elif signal == "STOP_LOSS" and shares > 0:
            sell_amount = shares * current_price
            fee = sell_amount * transaction_cost
            cash += (sell_amount - fee)
            trades.append({
                'date': df.iloc[i]['date'],
                'action': '清仓止损',
                'price': current_price,
                'shares': shares,
                'amount': sell_amount
            })
            shares = 0
            cost_basis = 0
        current_total = cash + (shares * current_price)
        portfolio_value.append(current_total)
    # 最终清仓
    if shares > 0:
        final_price = df.iloc[-1]['net_value']
        sell_amount = shares * final_price
        fee = sell_amount * transaction_cost
        cash += (sell_amount - fee)
        trades.append({
            'date': df.iloc[-1]['date'],
            'action': 'FINAL_SELL',
            'price': final_price,
            'shares': shares,
            'amount': sell_amount
        })
        final_total = cash
    else:
        final_total = portfolio_value[-1]
    # 计算收益率
    total_return = (final_total - initial_capital) / initial_capital * 100
    buy_hold_return = (df['net_value'].iloc[-1] - df['net_value'].iloc[period]) / df['net_value'].iloc[period] * 100
    # 输出回测结果
    print(f"回测结果:")
    print(f"初始资金: {initial_capital:.0f}元")
    print(f"最终资产: {final_total:.0f}元")
    print(f"总收益率: {total_return:+.2f}%")
    print(f"交易次数: {len(trades)}次")
    print(f"买入持有收益率: {buy_hold_return:+.2f}%")
    print(f"超额收益: {total_return - buy_hold_return:+.2f}%")
    # 显示交易记录
    if trades:
        print("\n交易记录:")
        for trade in trades:  # 只显示最近10次交易
            print(f" {trade['date'].strftime('%Y-%m-%d')} {trade['action']} {trade['shares']}股 @ {trade['price']:.3f}")
    # 创建走势图
    results_data = {
        '总收益率': total_return,
        '买入持有收益': buy_hold_return,
        '交易次数': len(trades),
        '超额收益': total_return - buy_hold_return
    }
    chart_path = f'分析表格/走势图/{name}({fund_c})回测分析.png'
    create_fund_chart(df, trades, name, fund_c, fund_t, results_data, chart_path)
    return {
        '基金名称': name,
        '基金代码': fund_c,
        '板块': fund_t,
        '总收益率': float(f'{total_return:.2f}'),
        '交易次数': len(trades),
        '买入持有收益': float(f'{buy_hold_return:.2f}'),
        '超额收益': float(f'{total_return - buy_hold_return:.2f}'),
        '数据量': len(df),
    }


# 获取信号
def generate_trading_signal_simple(current_price, lower_band, upper_band, ma20, cost_basis):
    """简化的交易信号生成"""
    # 计算当前盈亏比例（如果有持仓）
    if cost_basis > 0:
        profit_pct = ((current_price - cost_basis) / cost_basis) * 100
    else:
        profit_pct = 0
    # if cost_basis > 0 and profit_pct >= 5: # 添加止盈点为盈利5%
    #     return "SELL", profit_pct
    # 买入条件：价格触及布林带下轨，认为是相对低位
    # fuj1 = (ma20 - lower_band) * lower_band * 100
    if current_price <= lower_band:  # 价格低于或等于布林带下轨时买入and fuj1 > 5
        return "BUY", profit_pct
    # 卖出条件：价格突破布林带上轨50%，认为是相对高位
    elif current_price >= upper_band * 1.02:  # 价格高于上轨50%时卖出
        return "SELL", profit_pct
    # 止损条件：价格跌破20日均线且亏损超过8%
    elif current_price < ma20 and profit_pct < -8:  # 价格在均线下且亏损8%以上时止损
        return "STOP_LOSS", profit_pct
    else:
        return "HOLD", profit_pct  # 其他情况持有


if __name__ == '__main__':    # 读取基金数据文件
    jj_rc3 = pd.read_excel(r'../Fund_form_files/JJ_bd.xlsx', dtype={'基金代码': str, '基金名称': str})
    results = []
    print(f'表格基金数量: {len(jj_rc3["基金代码"].values)}')
    for fund_code in jj_rc3['基金代码'].values:
        try:
            fund_name = jj_rc3[jj_rc3['基金代码'] == fund_code]['基金名称'].iloc[0]
            # fund_type = jj_rc3[jj_rc3['基金代码'] == fund_code]['板块'].iloc[0]
            result = backtest_simple_strategy(fund_code, fund_name)
            if result:
                results.append(result)
        except Exception as e:
            print(f"基金 {fund_code} 回测失败: {e}")
            continue
    if results:
        print("\n" + "=" * 60)
        print("回测结果总结")
        print("=" * 60)
        results_df = pd.DataFrame(results)
        results_df = results_df.sort_values('总收益率', ascending=False)
        print(
            results_df[['基金名称', '基金代码', '总收益率', '买入持有收益', '超额收益', '交易次数', '板块']].to_string(
                index=False))
        # 保存结果到Excel文件

        results_df.to_excel('分析表格/(4年)基金回测结果(ma20指标半止盈0.8止损Low1.02).xlsx', index=False)
        print(f"\n详细结果已保存到 '分析表格/基金回测结果.xlsx'")
        # 显示最佳和最差表现
        best_fund = results_df.iloc[0]
        worst_fund = results_df.iloc[-1]
        print(f"\n🎯 最佳表现: {best_fund['基金名称']} - 收益率: {float(best_fund['总收益率']):+.1f}%")
        print(f"⚠️  最差表现: {worst_fund['基金名称']} - 收益率: {float(worst_fund['总收益率']):+.1f}%")
        print(f"📊 平均收益率: {results_df['总收益率'].mean():.1f}%")
        print(f"🔄 胜率(跑赢买入持有): {(results_df['超额收益'] > 0).sum()}/{len(results_df)}")
    print("回测完成！所有走势图已保存到 '分析表格/走势图/' 目录")
